Hex Workshop 16-bit v2.10
16-bit Cracking in WinNT
by ytc_ [tNO '99]

Target Hex Workshop 16-bit v2.10
URL Not available (but target can be found in ORCPAK2.ZIP at +Greythorne's website)
Tools used Softice v3.x for WinNT
  Win32 API Help (Optional)
Protection Serial Only
Level Beginners/Newbies
Introduction

It is a well known fact that when cracking in WinNT, there are a few limitations we have to cope with when cracking serial protections :-

Normally, in Win9x, one could use the 16-bit APIs when cracking 16-bit applications, such as GetDlgItemText, GetWindowText and even hmemcpy. Some might also prefer the 'task', 'hwnd [module-name]', 'bmsg [hwnd] wm_gettext' method to get to the protection scheme. Others would enter a bogus serial and do a 's 0 L FFFFFFFF "bogus_serial_here"'. The rest are hard-core 'hmemcpy', 'bpr' users which works about 99.9% in all applications. But, in WinNT, things are different with the limitations described above. What can we do? First, we need to know that when 16-bit programs are executed in WinNT, it is being thunked upwards first, then the normal 32-bit API calls are used. In Win9x, the process are the reverse, ie. 32-bit programs are thunked downwards first to 16-bit level. With this knowledge, we know that using the normal 32-bit APIs will also work when cracking 16-bit applications.

Essay

I will assume that you have already set up your copy of Softice and know how to use it well, including knowing what the shortcut function keys are (F8, F10, F11 and F12). If not, I suggest you read some other essays on how to set up Softice first before continuing. I will also assume that you have a fair knowledge of assembly language.

Like any other programs, run hworks16.exe and go to the about screen where the registration box is located. Notice that this is a serial only type of protection scheme. Enter some bogus numbers into the edit-box (I use my favourite '1123583145'), Ctrl-D into Softice and place breakpoints on GetDlgItemTextA and GetWindowTextA. Ctrl-D back to program (or F5) and click on the 'Register' button. Immediately, you find yourself at the beginning of the GetWindowTextA procedure. Press F11 once to get back to the caller and looking at the bottom bar of your code window, you immediately know that you are in the wow32.dll process.

Looking at EAX, you notice that the value returned is 9. Checking back with your Win32 API help guide, you should know that this not the break we are looking for because GetWindowTextA should return the length of my bogus serial, which is 0Ah (10d) in EAX. So, ignore this break and Ctrl-D or F5 again, followed by F11 until you see 0Ah in EAX (which is on the 5th break). At one point, you might need to move your mouse a little to continue. Press F12 a few times until you find yourself in HWORKS(03). Please take note the the addresses might be different from what you see on your screen.

15FF:254E  9AB3343703          CALL    0337:34B3
15FF:2553  EB10                JMP     2565 <= you land here
15FF:2555  50                  PUSH    AX
15FF:2556  C45E06              LES     BX,[BP+06]
15FF:2559  26FF7702            PUSH    WORD PTR ES:[BX+02]
15FF:255D  26FF37              PUSH    WORD PTR ES:[BX]
15FF:2560  9AD4144710          CALL    1047:14D4
15FF:2565  5E                  POP     SI
15FF:2566  C9                  LEAVE
15FF:2567  CA0800              RETF    0008

At this point, you notice that there is a RETF instruction just a few lines below. Immediately you know that you are not nearing the protection scheme yet. You press F12 a few more times and stop when you encounter the first conditional jump.

166F:15AD  9A3826BF16          CALL    16BF:2638
166F:15B2  8E4608              MOV     ES,[BP+08] <== you land here
166F:15B5  26FF743A            PUSH    WORD PTR ES:[SI+3A] ; segment to bogus serial
166F:15B9  26FF7438            PUSH    WORD PTR ES:[SI+38] ; offset to bogus serial
166F:15BD  8D46EC              LEA     AX,[BP-14]
166F:15C0  16                  PUSH    SS ; segment 2
166F:15C1  50                  PUSH    AX ; offset 2
166F:15C2  9A3E75AF15          CALL    15AF:753E ; copies bogus serial to SS:AX
166F:15C7  83C408              ADD     SP,08
166F:15CA  8D46EC              LEA     AX,[BP-14]
166F:15CD  16                  PUSH    SS
166F:15CE  50                  PUSH    AX ; SS:AX points to bogus serial
166F:15CF  9A381FAF15          CALL    15AF:1F38 <= !!!
166F:15D4  83C404              ADD     SP,04
166F:15D7  0BC0                OR      AX,AX <= !!!
166F:15D9  7552                JNZ     162D <= !!!!!!!!!!
166F:15DB  6A75                PUSH    75
166F:15DD  6A01                PUSH    01
166F:15DF  50                  PUSH    AX
166F:15E0  50                  PUSH    AX
166F:15E1  50                  PUSH    AX
166F:15E2  8D46C0              LEA     AX,[BP-40]
166F:15E5  16                  PUSH    SS
166F:15E6  50                  PUSH    AX
166F:15E7  9A2E126F16          CALL    166F:122E
166F:15EC  8D46C0              LEA     AX,[BP-40]
166F:15EF  16                  PUSH    SS
166F:15F0  50                  PUSH    AX
166F:15F1  9A4431BF16          CALL    16BF:3144

With enough experience, one can guess immediately that the unconditional jump at 166F:15D9 is the 'deciding' jump between 'bad_cracker' and 'good_cracker'. And, the call above it must be the location of the serial generating routine. Now, a lazy cracker might stop here and change the 'jnz 162D' to a 'jz 162D', recieve the 'congratulations, you cracked my program' message, risking the possibility that there might be another check somewhere. I choose the better, but slightly harder way... find a valid serial!

By now, you should have cleared all breakpoints (bc *) and placed a fresh new one on 166F:15CF. Trace into this call (F8) and you look around for clues.

15AF:1F38  C8140000            ENTER   0014,00 ; beginning of routine
...
15AF:1F3E  C47E06              LES     DI,[BP+06] ; loads offset of bogus serial
15AF:1F41  B9FFFF              MOV     CX,FFFF
15AF:1F44  33C0                XOR     AX,AX
15AF:1F46  F2AE                REPNZ SCASB ; standard scheme to retrieve length of string
15AF:1F48  F7D1                NOT     CX
15AF:1F4A  49                  DEC     CX ; CX now contains length of bogus serial
15AF:1F4B  83F908              CMP     CX,08 ; compares serial length with 8
15AF:1F4E  7575                JNZ     1FC5 ; jump to end of routine!!
...
15AF:1F6B  52                  PUSH    DX
15AF:1F6C  50                  PUSH    AX
15AF:1F6D  0E                  PUSH    CS
15AF:1F6E  E8F3FE              CALL    1E64 ; interesting call!!
...
15AF:1FA2  B90200              MOV     CX,0002
15AF:1FA5  8BF8                MOV     DI,AX
15AF:1FA7  8D76FC              LEA     SI,[BP-04]
15AF:1FAA  8EC2                MOV     ES,DX
15AF:1FAC  D1E9                SHR     CX,1
15AF:1FAE  1BC0                SBB     AX,AX
15AF:1FB0  3BC9                CMP     CX,CX
15AF:1FB2  F3A7                REPZ CMPSW ; compare!!
15AF:1FB4  7504                JNZ     1FBA
15AF:1FB6  2BC8                SUB     CX,AX
15AF:1FB8  F3A6                REPZ CMPSB
15AF:1FBA  7406                JZ      1FC2
15AF:1FBC  33C0                XOR     AX,AX ; clear AX flag!!
15AF:1FBE  5E                  POP     SI
15AF:1FBF  5F                  POP     DI
15AF:1FC0  C9                  LEAVE
15AF:1FC1  CB                  RETF
15AF:1FC2  B80100              MOV     AX,0001 ; set AX flag!!
15AF:1FC5  5E                  POP     SI
15AF:1FC6  5F                  POP     DI
15AF:1FC7  C9                  LEAVE
15AF:1FC8  CB                  RETF

A-hah! Right at the heart of the scheme! First, it checks if your serial is 8 digits long, and 'jump bad_cracker' if it isn't. Then the line I marked as 'interesting call' calculates a 2-character string based on the 3-8th characters of the bogus serial. Then compares it with the FIRST 2 DIGITS OF BOGUS SERIAL and jump if it not equal. You now clear all your breakpoints and place another fresh new one right on the compare instruction, then restart the registration process, this time using a 8-digit bogus serial (I truncated mine to 11235831). You break right on the compare instruction. Do a 'd es:di' and you see your bogus serial. Do a 'd ds:si' and you see the magic characters (in my case, it is HX). Restart the program again, this time with the CORRECT SERIAL. Before clicking on 'Register', you disable all your breakpoints (bd *). DO NOT CLEAR THEM YET, JUST IN CASE. Now you click on 'Register' and BINGO!! You see the 'Thank you for cracking my program successfully' message, enter your name and organization, and enjoy the program as long as you like!

Final Notes

The reason I choose an old version of Hex Workshop is because we need to first study old protection schemes in order to grasp a main idea of the art of cracking because older protection schemes are normally easier than the newer and modern ones. Who knows, maybe this tutorial might shed some light on some of you who are still struggling with the latest version of Hex Workshop.

Finally, after cracking this program, keep the serial number to yourself and remove the program. If you want the program, buy it and support the authors who deserves it for all the work they have done.

Greets

There's a lot of people that I know, so I'll just greet everyone, especially those in #tno, #win32asm, #cracking4newbies and #cracking at EFNet.

Email     : y_t_c@usa.net
Website : http://ytc98.cjb.net